/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#ifdef WIN32

#include "WMIClient.h"
#include "log/Log.h"
#include "common/utils/Win32Utils.h"
#include <sstream>
#include <iomanip>
#include <windows.h>
#include <iostream>
#include <string>

namespace {
    constexpr int32_t COMMON_RETRY_TIME = 5;
} /* namespace */

std::shared_ptr<WMIClient> WMIClient::GetInstance()
{
    std::shared_ptr<WMIClient> instance = std::make_shared<WMIClient>();
    return instance;
}

WMIClient::~WMIClient()
{
    INFOLOG("Release resource");
    if (m_pSvc != nullptr) {
        m_pSvc->Release();
    }
    if (m_pLoc != nullptr) {
        m_pLoc->Release();
    }
    CoUninitialize();
}

int32_t WMIClient::Init()
{
    if (m_pSvc != nullptr && m_pVirutalSysMgrService != nullptr) {
        INFOLOG("Already init");
        return SUCCESS;
    }
    INFOLOG("Start init");
    HRESULT hres;
    // Step 1: --------------------------------------------------
    // Initialize COM. ------------------------------------------

    hres = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (FAILED(hres)) {
        ERRLOG("Failed to initialize COM library. Error code = %llu", hres);
        return FAILED;
    }

    // Step 2: --------------------------------------------------
    // Set general COM security levels --------------------------

    hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IDENTIFY, NULL,
        EOAC_NONE, NULL);
    if (FAILED(hres)) {
        ERRLOG("Failed to initialize security. Error code = %llu", hres);
        CoUninitialize();
        return FAILED;
    }
    if (InitLocate() != SUCCESS) {
        CoUninitialize();
        return FAILED;
    }

    return GetWMIProxyClassObj(m_pVirutalSysMgrService, "Msvm_VirtualSystemManagementService");
}

int32_t WMIClient::InitLocate()
{
    // Step 3: ---------------------------------------------------
    // Obtain the initial locator to WMI -------------------------

    HRESULT hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&m_pLoc);
    if (FAILED(hres)) {
        ERRLOG("Failed to create IWbemLocator object. Error code = %llu", hres);
        CoUninitialize();
        return FAILED; // Program has failed.
    }

    // Step 4: -----------------------------------------------------
    // Connect to WMI through the IWbemLocator::ConnectServer method

    // Connect to the root\cimv2 namespace with
    // the current user and obtain pointer pSvc
    // to make IWbemServices calls.
    hres = m_pLoc->ConnectServer(_bstr_t(L"root\\virtualization\\v2"), NULL, NULL, 0, NULL, NULL, 0, &m_pSvc);
    if (FAILED(hres)) {
        ERRLOG("Could not connect. Error code = %llu", hres);
        m_pLoc->Release();
        CoUninitialize();
        return FAILED;
    }
    DBGLOG("connect success");

    hres = CoSetProxyBlanket(m_pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL,
        RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
    if (FAILED(hres)) {
        ERRLOG("Could not set proxy blanket. Error code = %llu", hres);
        m_pSvc->Release();
        m_pLoc->Release();
        CoUninitialize();
        return FAILED;
    }
    return SUCCESS;
}

/**
 * @brief 验证异步任务执行结果
 *
 * @param jobPath   [IN]任务完整路径
 * 例：\\A200170229V\root\virtualization\v2:Msvm_ConcreteJob.InstanceID="35EDE2D3-797A-4ECC-9632-D4120927ADE5"
 * @param errorDetail   [OUT]详细错误内容
 * @return 错误码：0 成功, -1  失败
 */
int32_t WMIClient::CheckAsynchJobResult(VARIANT &jobPath, std::string &errorDetail)
{
    INFOLOG("Asynjob: %ls", jobPath.bstrVal);
    std::vector<IWbemClassObject *> pJobs;
    std::string jobPathStr = _com_util::ConvertBSTRToString(jobPath.bstrVal);
    std::string sql = "SELECT * FROM Msvm_ConcreteJob WHERE __PATH = '" +
        Module::CMpString::StrReplace(jobPathStr, "\\", "\\\\") + "'";
    if (FAILED(ExecQuery(pJobs, sql, true))) {
        ERRLOG("CheckAsynchJobResult ExecQuery failed.");
        return FAILED;
    }
    if (pJobs.empty()) {
        ERRLOG("Query Msvm_ConcreteJob result is empty.");
        return FAILED;
    }
    VARIANT status;
    if (GetObjectAttributeInBSTR(pJobs[0], "Status", status) != SUCCESS) {
        ERRLOG("CheckAsynchJobResult GetObjectAttributeInBSTR failed.");
        return FAILED;
    }
    // 判断任务是否执行成功
    if (strcmp(_com_util::ConvertBSTRToString(status.bstrVal), "OK") != 0) {
        VARIANT errorDescription;
        if (GetObjectAttributeInBSTR(pJobs[0], "ErrorDescription", errorDescription) != SUCCESS) {
            ERRLOG("CheckAsynchJobResult GetObjectAttributeInBSTR failed.");
            return FAILED;
        }
        errorDetail = _com_util::ConvertBSTRToString(errorDescription.bstrVal);
        ERRLOG("CheckAsynchJobResult job result is not ok, job result:%ls.", status.bstrVal);
        return FAILED;
    }
    return SUCCESS;
}

/**
 * @brief 验证函数执行结果
 *
 * @param pOutParam   [IN]函数返回结果
 * @param pCallResult   [IN]半异步调用结果
 * @param errorDetail   [OUT]详细错误内容
 * @return 错误码：0 成功, -1  失败
 */
int32_t WMIClient::CheckExecuteMethodResult(CComPtr<IWbemClassObject> pOutParam, CComPtr<IWbemCallResult> pCallResult,
    std::string &errorDetail)
{
    // 异步job判断条件
    int32_t asynchNum = 4096;
    long result = 0;

    VARIANT rc;
    if (FAILED(pOutParam->Get(L"ReturnValue", 0, &rc, NULL, NULL))) {
        errorDetail = "Get ReturnValue failed!";
        ERRLOG("CheckExecuteMethodResult get ReturnValue failed.");
        return FAILED;
    }
    int returnCode = rc.llVal;
    if (returnCode != 0 && returnCode != asynchNum) {
        errorDetail = "ExecMethod failed, return code: " + std::to_string(returnCode);
        ERRLOG("CheckExecuteMethodResult job failed. Error detail is: %s", errorDetail.c_str());
        return FAILED;
    }

    // 现阶段为同步方法，该调用不会失败
    if (pCallResult->GetCallStatus(m_maxTimeOut, &result) != SUCCESS) {
        errorDetail = "ExecMethod failed timeout";
        ERRLOG("CheckExecuteMethodResult GetCallStatus failed.");
        return FAILED;
    }

    // 获取job路径
    VARIANT jobPath;
    if (FAILED(pOutParam->Get(L"Job", 0, &jobPath, NULL, NULL))) {
        std::string pathStr = _com_util::ConvertBSTRToString(jobPath.bstrVal);
        errorDetail = "Get JOB failed or job path is empty, job path:" + pathStr;
        ERRLOG("CheckExecuteMethodResult get jobPath failed.");
        return FAILED;
    }
    // 异步任务
    if (rc.llVal == asynchNum) {
        return CheckAsynchJobResult(jobPath, errorDetail);
    }
    if (rc.llVal != 0) {
        errorDetail = "ExecMethod failed, return code: " + rc.llVal;
        ERRLOG("CheckExecuteMethodResult job failed.");
        return FAILED;
    }
    return SUCCESS;
}

/**
 * @brief 执行函数前准备工作
 *
 * @param param   [IN]函数执行参数
 * @param pMethodInstance   [OUT]函数实例对象
 * @param classInstancePath   [OUT]类实例完整地址
 * @return 错误码：0 成功, -1  失败
 */
int32_t WMIClient::ExecMethodPrepare(WmiMethodExecuteParam &param, IWbemClassObject *&pMethodInstance,
    VARIANT &classInstancePath)
{
    if (GetMethodParamCollector(pMethodInstance, param.methodOwnerClass, param.methodName) != SUCCESS) {
        param.errDetails = "GetMethodParamCollector failed";
        ERRLOG("ExecMethod GetMethodParamCollector failed.");
        return FAILED;
    }
    if (AddParamsToParamCollector(param.inputeParams, pMethodInstance) != SUCCESS) {
        param.errDetails = "Put param failed";
        ERRLOG("ExecMethod AddParamsToParamCollector failed.");
        return FAILED;
    }
    std::vector<IWbemClassObject *> pClassInstances;
    std::string sql = "SELECT * FROM " + param.methodOwnerClass;
    if (ExecQuery(pClassInstances, sql, true) != SUCCESS) {
        param.errDetails = "Get " + param.methodOwnerClass + " instance failed";
        ERRLOG("ExecMethod ExecQuery failed, sql:%s", sql.c_str());
        return FAILED;
    }
    if (pClassInstances.empty()) {
        ERRLOG("Query ClassInstances result is empty.");
        return FAILED;
    }
    IWbemClassObject *pClassInstance = pClassInstances[0];
    if (pClassInstance->Get(L"__Path", 0, &classInstancePath, 0, 0) != SUCCESS) {
        param.errDetails = "Get classInstancePath failed";
        ERRLOG("ExecMethod Get classInstancePath failed");
        return FAILED;
    }
    return SUCCESS;
}

/**
 * @brief 执行函数
 *
 * @param param   [IN]函数执行参数
 * @param pOutParam   [OUT]函数返回结果
 * @return 错误码：0 成功, -1  失败
 */
int32_t WMIClient::ExecMethod(WmiMethodExecuteParam &param, CComPtr<IWbemClassObject> &pOutParam)
{
    IWbemClassObject *pMethodInstance = nullptr;
    VARIANT classInstancePath;
    if (ExecMethodPrepare(param, pMethodInstance, classInstancePath) != SUCCESS) {
        ERRLOG("ExecMethod ExecMethodPrepare failed");
        return FAILED;
    }
    IWbemCallResult *pCallResult = nullptr;
    // WBEM_FLAG_RETURN_WBEM_COMPLETE:0 同步， WBEM_FLAG_RETURN_IMMEDIATELY:16  异步
    HRESULT hr = m_pSvc->ExecMethod(classInstancePath.bstrVal, _com_util::ConvertStringToBSTR(param.methodName.c_str()),
        WBEM_FLAG_RETURN_WBEM_COMPLETE, NULL, pMethodInstance, &pOutParam, &pCallResult);
    if (FAILED(hr)) {
        ERRLOG("ExecMethod %s failed, error code: %x", param.methodName.c_str(), hr);
        param.errDetails = "Execute Method " + param.methodName + "failed, error code is:" + std::to_string(hr);
        return FAILED;
    }
    if (CheckExecuteMethodResult(pOutParam, pCallResult, param.errDetails) == FAILED) {
        ERRLOG("ExecMethod CheckExecuteMethodResult failed, error detail is: %s", param.errDetails.c_str());
        return FAILED;
    }
    return SUCCESS;
}

int32_t WMIClient::ExecMethodOnSpecifiedObject(WmiMethodExecuteParam &param, CComPtr<IWbemClassObject> &pOutParam,
                                               const VARIANT& obj)
{
    IWbemClassObject *pMethodInstance = nullptr;
    VARIANT classInstancePath;
    if (ExecMethodPrepare(param, pMethodInstance, classInstancePath) != SUCCESS) {
        ERRLOG("ExecMethod ExecMethodPrepare failed");
        return FAILED;
    }
    IWbemCallResult *pCallResult = nullptr;
    // WBEM_FLAG_RETURN_WBEM_COMPLETE:0 同步， WBEM_FLAG_RETURN_IMMEDIATELY:16  异步
    HRESULT hr = m_pSvc->ExecMethod(obj.bstrVal, _com_util::ConvertStringToBSTR(param.methodName.c_str()),
        WBEM_FLAG_RETURN_WBEM_COMPLETE, NULL, pMethodInstance, &pOutParam, &pCallResult);
    if (FAILED(hr)) {
        ERRLOG("ExecMethod %s failed, error code: %x", param.methodName.c_str(), hr);
        param.errDetails = "Execute Method " + param.methodName + "failed, error code is:" + std::to_string(hr);
        return FAILED;
    }
    if (CheckExecuteMethodResult(pOutParam, pCallResult, param.errDetails) == FAILED) {
        ERRLOG("ExecMethod CheckExecuteMethodResult failed, error detail is: %s", param.errDetails.c_str());
        return FAILED;
    }
    return SUCCESS;
}

/**
 * @brief 执行SQL查询
 *
 * @param result   [OUT]查询结果数组
 * @param queryCmd   [IN]sql语句
 * @param returnFindingOne   [IN]是否返回单个结果
 * @return 错误码：0 成功, -1  失败
 */
int32_t WMIClient::ExecQuery(std::vector<IWbemClassObject *> &result, const std::string queryCmd, bool returnFindingOne)
{
    INFOLOG("Start to exce query command: %s.", WIPE_SENSITIVE(queryCmd).c_str());
    ULONG pReturn1 = 0;
    IEnumWbemClassObject *pResult = nullptr;
    HRESULT hr = m_pSvc->ExecQuery(BSTR(L"WQL"), _com_util::ConvertStringToBSTR(queryCmd.c_str()),
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pResult);
    if (FAILED(hr)) {
        ERRLOG("Execute sql %s failed", WIPE_SENSITIVE(queryCmd).c_str());
        return FAILED;
    }
    while (SUCCEEDED(hr)) {
        IWbemClassObject *obj;
        hr = pResult->Next(WBEM_INFINITE, 1, &obj, &pReturn1);
        if (WBEM_S_FALSE == hr) {
            INFOLOG("Find next failed, break out");
            break;
        }
        if (FAILED(hr)) {
            ERRLOG("Find next failed, error code is %llu, sql is %s", hr, WIPE_SENSITIVE(queryCmd).c_str());
            return FAILED;
        }
        result.push_back(obj);
        if (returnFindingOne) {
            break;
        }
    }
    return SUCCESS;
}

/**
 * @brief 获取wmi对象
 *
 * @param instPtr   [OUT]wmi对象
 * @param className   [IN]类名
 * @return 错误码：0 成功, -1  失败
 */
int32_t WMIClient::GetWMIProxyClassObj(IWbemClassObject *&instPtr, const std::string &className)
{
    BSTR classNameStr = _com_util::ConvertStringToBSTR(className.c_str());
    if (m_pSvc->GetObject(classNameStr, 0, NULL, &instPtr, NULL) != SUCCESS) {
        ERRLOG("Get %s  failed", className.c_str());
        return FAILED;
    }
    return SUCCESS;
}

/**
 * @brief 获取函数实例对象
 *
 * @param instPtr   [OUT]函数实例对象
 * @param className   [IN]类名
 * @param methodName   [IN]函数名
 * @return 错误码：0 成功, -1  失败
 */
int32_t WMIClient::GetMethodParamCollector(IWbemClassObject *&instPtr, const std::string &className,
    const std::string &methodName)
{
    IWbemClassObject *pClass;
    if (GetWMIProxyClassObj(pClass, className) != 0) {
        ERRLOG("GetMethodParamCollector GetWMIProxyClassObj failed");
        return FAILED;
    }
    BSTR methodNameBstr = _com_util::ConvertStringToBSTR(methodName.c_str());
    IWbemClassObject *methodClass = nullptr;
    if (pClass->GetMethod(methodNameBstr, 0, &methodClass, nullptr) != SUCCESS) {
        ERRLOG("Get class %s's mehod %s failed", className.c_str(), methodName.c_str());
        return FAILED;
    }
    IWbemClassObject *pMethodImportSystemDefinitionInstance = nullptr;
    if (methodClass->SpawnInstance(0, &instPtr) != SUCCESS) {
        ERRLOG("SpawnInstance mehod %s failed", methodName.c_str());
        return FAILED;
    }
    return SUCCESS;
}

/**
 * @brief 函数实例对象的参数赋值
 *
 * @param params   [IN]需要赋值的参数数组
 * @param paramCollector   [IN]wmi对象指针
 * @return 错误码：0 成功, -1  失败
 */
int32_t WMIClient::AddParamsToParamCollector(std::vector<WmiMethodParam> &params, IWbemClassObject *paramCollector)
{
    for (auto &param : params) {
        HRESULT hr = paramCollector->Put(param.name, 0, &param.value, param.type);
        INFOLOG("Put param %ls", param.name);
        if (hr != SUCCESS) {
            ERRLOG("Put param %ls failed, rc: 0x%x", param.name, hr);
            return FAILED;
        }
    }
    IWbemContext *ppCtx = 0;
    IWbemCallResult *ppResult = 0;
    if (m_pSvc->PutInstance(paramCollector, 0, ppCtx, &ppResult) != SUCCESS) {
        ERRLOG("PutInstance failed");
        return FAILED;
    }
    return SUCCESS;
}

/**
 * @brief 获取WMI对象的文本表示形式
 *
 * @param format   [IN]用于表示对象的文本格式的定义 （目前只有1或2）
 * @param obj   [IN]wmi对象指针
 * @param resultString [OUT]对象的文本表示形式
 * @return 错误码：0 成功, -1  失败
 */
int32_t WMIClient::GetObjectTextInSpecifiedFormat(int32_t format, IWbemClassObject *obj, std::string &resultString)
{
    try {
        IWbemObjectTextSrc *pTxt = nullptr;
        HRESULT hres =
            CoCreateInstance(CLSID_WbemObjectTextSrc, 0, CLSCTX_INPROC_SERVER, IID_IWbemObjectTextSrc, (LPVOID *)&pTxt);
        BSTR resultText;
        if (pTxt->GetText(0, obj, format, NULL, &resultText) != SUCCESS) {
            ERRLOG("GetText failed");
            return FAILED;
        }
        int byteLen = SysStringByteLen(resultText);
        resultString = BSTRToString(resultText);

        std::string s1 = _com_util::ConvertBSTRToString(resultText);
        std::string s2 = BSTRToString(resultText);
    } catch (std::exception& e) {
        ERRLOG("GetObjectTextInSpecifiedFormat Exception caught: %s", e.what());
        return FAILED;
    }
    return SUCCESS;
}

/**
 * @brief 获取WMI对象属性
 *
 * @param obj   [IN]wmi对象指针
 * @param key   [IN]属性名称
 * @param attributeValue [OUT]VARIANT对象
 * @return 错误码：0 成功, -1  失败
 */
int32_t WMIClient::GetObjectAttributeInBSTR(IWbemClassObject *obj, const std::string &key, VARIANT &attributeValue)
{
    VariantInit(&attributeValue);
    V_VT(&attributeValue) = VT_BSTR;
    BSTR temp = _com_util::ConvertStringToBSTR(key.c_str());
    if (obj->Get(temp, 0, &attributeValue, 0, 0) != SUCCESS) {
        ERRLOG("GetObjectAttribute %s failed", key.c_str());
        return FAILED;
    }
    BSTR attributeValueBSTR = V_BSTR(&attributeValue);
    std::string s1 = _com_util::ConvertBSTRToString(attributeValueBSTR);
    std::string s2 = BSTRToString(attributeValueBSTR);
    return SUCCESS;
}

/**
 * @brief 获取WMI对象数组属性
 *
 * @param obj   [IN]wmi对象指针
 * @param key   [IN]属性名称
 * @param result [OUT]BSTR数组
 * @return 错误码：0 成功, -1  失败
 */
int32_t WMIClient::GetObjectAttributeInBSTRARRAY(IWbemClassObject *obj, const std::string &key,
    std::vector<BSTR> &result)
{
    ERRLOG("GetObjectAttributeInBSTRARRAY key:%s", key.c_str());
    VARIANT ResultingResourceSettings;
    BSTR temp = _com_util::ConvertStringToBSTR(key.c_str());
    if (obj->Get(temp, 0, &ResultingResourceSettings, NULL, NULL)) {
        ERRLOG("GetObjectAttribute %s failed", key.c_str());
        return FAILED;
    }

    BSTR createdResourcePath;
    if (ResultingResourceSettings.vt == (VT_ARRAY | VT_BSTR)) {
        SAFEARRAY *psa = ResultingResourceSettings.parray;
        long lLBound = 0;
        long lUBound = 2;
        SafeArrayGetLBound(psa, 1, &lLBound);
        SafeArrayGetUBound(psa, 1, &lUBound);
        std::string diskDrivePath;
        for (long i = lLBound; i <= lUBound; i++) {
            SafeArrayGetElement(psa, &i, &createdResourcePath);
            result.push_back(createdResourcePath);
        }
    }
    return SUCCESS;
}

/**
 * @brief vector数组转为VARIANT类型数组
 *
 * @param input   [IN]字符串数组
 * @param result [OUT]VARIANT对象
 * @return 错误码：0 成功, -1  失败
 */
int32_t WMIClient::GetBSTRArray(const std::vector<std::string> &input, VARIANT &result)
{
    SAFEARRAY *psa = SafeArrayCreateVector(VT_BSTR, 0, input.size());
    long index = 0;
    for (const auto &inputParm : input) {
        BSTR bstrTmp = StringToBSTR(inputParm);
        CComBSTR temp(bstrTmp);
        OLECHAR *restr = temp.Detach();
        if (SafeArrayPutElement(psa, &index, restr) != SUCCESS) {
            ERRLOG("SafeArrayPutElement failed");
            return FAILED;
        }
        index++;
    }
    VariantInit(&result);
    V_VT(&result) = VT_ARRAY | VT_BSTR;
    V_ARRAY(&result) = psa;
    return SUCCESS;
}

int32_t WMIClient::SpiltVMDisk(IWbemClassObject *classObj, HyperVPlugin::VolumeExtendInfo &diskInfo)
{
    VARIANT diskInstanceId;
    VariantInit(&diskInstanceId);
    if (GetObjectAttributeInBSTR(classObj, "InstanceID", diskInstanceId) == FAILED) {
        ERRLOG("SpiltVMDisk GetObjectAttributeInBSTR failed");
        return FAILED;
    }

    BSTR diskInstanceIdBSTR = V_BSTR(&diskInstanceId);
    std::string s1 = _com_util::ConvertBSTRToString(diskInstanceIdBSTR);
    std::string s2 = BSTRToString(diskInstanceIdBSTR);
    std::vector<std::string> strList;
    // diskInstanceId格式"Microsoft:F62027AD-E793-4CCB-BBAD-BBF596C29E72\83F8638B-8DCA-4152-9EDA-2CA8B33039B4\0\0\D"
    // 从左往右依次为虚拟机ID、控制器ID、控制器序号、控制器槽位号
    Module::CMpString::StrSplit(strList, _com_util::ConvertBSTRToString(V_BSTR(&diskInstanceId)), '\\');
    int32_t spiltArraySize = 4;
    if (strList.size() < spiltArraySize) {
        ERRLOG("Spilt disk info failed");
        return FAILED;
    }
    int32_t index = 1;
    diskInfo.m_controllerUUID = strList[index++];
    diskInfo.m_controllerIndex = strList[index++];
    diskInfo.m_controllerPos = strList[index++];
    return SUCCESS;
}

int32_t WMIClient::AttachDiskToVM(const HyperVPlugin::VolumeExtendInfo& diskInfo, const std::string& vmId)
{
    if (Init() == FAILED) {
        ERRLOG("Init failed");
        return FAILED;
    }
    if (CreateLogicDiskOrDiskDrive(diskInfo, vmId, true, m_DiskDrivePath) != 0) {
        ERRLOG("CreateDiskDrive failed");
        return FAILED;
    }
    if (CreateLogicDiskOrDiskDrive(diskInfo, vmId, false, m_LogicDiskPath) != 0) {
        ERRLOG("CreateLogicDisk failed");
        return FAILED;
    }
    return SUCCESS;
}


int32_t WMIClient::AddLogicDiskOrDiskDrive(const VARIANT &vmSettingPath, const VARIANT &resourceSettingsArray,
    BSTR &createdResourcePath)
{
    std::vector<WmiMethodParam> inputeParams;
    WmiMethodParam affectedConfiguration(L"AffectedConfiguration", CIM_REFERENCE, vmSettingPath);
    inputeParams.push_back(affectedConfiguration);
    WmiMethodParam resourceSettings(L"ResourceSettings", CIM_STRING | CIM_FLAG_ARRAY, resourceSettingsArray);
    inputeParams.push_back(resourceSettings);

    WmiMethodExecuteParam executeParam("Msvm_VirtualSystemManagementService", "AddResourceSettings");
    executeParam.inputeParams = inputeParams;
    CComPtr<IWbemClassObject> pOutParam = nullptr;
    int retryCount = Module::ConfigReader::getInt("HyperVConfig", "WmiApiRetryCount");
    int retryInterval = Module::ConfigReader::getInt("HyperVConfig", "WmiApiRetryInterval");

    int ret = FAILED;
    for (int i = 0; i < retryCount; i++) {
        ret = ExecMethod(executeParam, pOutParam);
        if (ret == SUCCESS) {
            INFOLOG("ExecuteMethod mehod %s SUCCESS!", executeParam.methodName.c_str());
            break;
        }
        ERRLOG("ExecuteMethod mehod %s failed, retry in %d seconds", executeParam.methodName.c_str(),
            retryInterval);
        VirtPlugin::Utils::SleepSeconds(retryInterval);
    }
    if (ret != SUCCESS) {
        ERRLOG("ExecuteMethod mehod %s failed, error detail: %s", executeParam.methodName.c_str(),
            executeParam.errDetails.c_str());
        return FAILED;
    }
    std::vector<BSTR> result;
    if (GetObjectAttributeInBSTRARRAY(pOutParam, "ResultingResourceSettings", result) != SUCCESS) {
        ERRLOG("CreateLogicDiskOrDiskDrive GetObjectAttributeInBSTRARRAY failed");
        return FAILED;
    }
    if (result.size() != 1) {
        ERRLOG("Count not right");
        return FAILED;
    }
    createdResourcePath = result[0];
    return SUCCESS;
}

int32_t WMIClient::CreateLogicDiskOrDiskDrive(const HyperVPlugin::VolumeExtendInfo& diskInfo, const std::string& vmId,
    const bool diskDrive, BSTR& createdResourcePath)
{
    if (GetWMIProxyClassObj(m_pStorageAllocationSettingData, "Msvm_StorageAllocationSettingData") != SUCCESS) {
        ERRLOG("Get Msvm_StorageAllocationSettingData obj failed");
        return FAILED;
    }
    int32_t formatNum = 2;
    VARIANT path;
    if (m_pStorageAllocationSettingData == nullptr) {
        ERRLOG("m_pStorageAllocationSettingData is nullptr");
        return FAILED;
    }
    IWbemClassObject *pDiskDriver = nullptr;
    if (m_pStorageAllocationSettingData->SpawnInstance(0, &pDiskDriver) != SUCCESS) {
        ERRLOG("SpawnInstance failed");
        return FAILED;
    }
    // Query controller
    std::vector<IWbemClassObject *> controllers;
    if (RetryToQueryControler(diskInfo, vmId, controllers) != SUCCESS) {
        ERRLOG("RetryToQueryControler failed.");
        return FAILED;
    }
    IWbemClassObject *controller = controllers[0];

    if (PrepareDiskDriver(diskInfo, controller, diskDrive, pDiskDriver) != SUCCESS) {
        ERRLOG("CreateLogicDiskOrDiskDrive PrepareController failed");
        return FAILED;
    }

    VARIANT resourceSettingsArray;
    std::string diskDriveInStr;
    GetObjectTextInSpecifiedFormat(formatNum, pDiskDriver, diskDriveInStr);

    std::vector<std::string> input;
    input.push_back(diskDriveInStr);
    if (GetBSTRArray(input, resourceSettingsArray) != SUCCESS) {
        ERRLOG("CreateLogicDiskOrDiskDrive GetBSTRArray failed");
        return FAILED;
    }

    VARIANT vmSettingPath;
    if (PrepareVMSettings(vmId, vmSettingPath) != SUCCESS) {
        ERRLOG("CreateLogicDiskOrDiskDrive PrepareVMSettings failed");
        return FAILED;
    }
    return AddLogicDiskOrDiskDrive(vmSettingPath, resourceSettingsArray, createdResourcePath);
}

int32_t WMIClient::RetryToQueryControler(const HyperVPlugin::VolumeExtendInfo& diskInfo, const std::string& vmId,
    std::vector<IWbemClassObject *> &controllers)
{
    std::string controllerIndex = diskInfo.m_controllerType == "1" ? "0" : diskInfo.m_controllerIndex;
    std::string sql = "SELECT * FROM CIM_ResourceAllocationSettingData where __RELPATH like '%" + vmId + "%" +
        diskInfo.m_controllerUUID + "__" + controllerIndex +
        "%' and (resourcetype=" + std::to_string(static_cast<int>(HyperVControllerType::IDE)) +
        " or resourcetype=" + std::to_string(static_cast<int>(HyperVControllerType::SCSI)) + ")";
    int sleepTime = 2;
    for (int index = 0; index < COMMON_RETRY_TIME; ++index) {
        if (ExecQuery(controllers, sql, true) != SUCCESS) {
            ERRLOG("CreateLogicDiskOrDiskDrive ExecQuery failed");
            return FAILED;
        }
        if (!controllers.empty()) {
            INFOLOG("Query CIM_ResourceAllocationSettingData result success.");
            break;
        }
        WARNLOG("Query CIM_ResourceAllocationSettingData result is empty.");
        if (index == COMMON_RETRY_TIME - 1) {
            return FAILED;
        }
        VirtPlugin::Utils::SleepSeconds(sleepTime);
    }
    return SUCCESS;
}


VARIANT WMIClient::Bool2BoolVariant(const bool b)
{
    VARIANT v;
    VariantInit(&v);
    V_VT(&v) = VT_BOOL;
    V_BOOL(&v) = b;
    return v;
}

std::string WMIClient::ConvertSlash(const std::string& input)
{
    std::string result;
    for (auto c : input) {
        if (c == '/') {
            result += '\\';
        }  else {
            result += c;
        }
    }
    return result;
}

void WMIClient::PrepareDriver(std::vector<WmiMethodParam> &params, const VARIANT &controller_path,
    const HyperVPlugin::VolumeExtendInfo &diskInfo)
{
    WmiMethodParam parent(L"Parent", CIM_STRING, controller_path);
    VARIANT vAddressOnParent = String2BSTRVariant(diskInfo.m_controllerPos);
    WmiMethodParam addressOnParent(L"addressOnParent", CIM_STRING, vAddressOnParent);
    VARIANT vResourceType = String2BSTRVariant(std::to_string(static_cast<int>(HyperVResourceType::DISK_DRIVE)));
    WmiMethodParam resourceType(L"ResourceType", CIM_UINT16, vResourceType);
    VARIANT vResourceSubType = String2BSTRVariant("Microsoft:Hyper-V:Synthetic Disk Drive");
    WmiMethodParam resourceSubType(L"ResourceSubType", CIM_STRING, vResourceSubType);
    VARIANT vCaption = String2BSTRVariant("Hard Drive");
    WmiMethodParam caption(L"Caption", CIM_STRING, vCaption);
    params.push_back(parent);
    params.push_back(addressOnParent);
    params.push_back(resourceType);
    params.push_back(resourceSubType);
    params.push_back(caption);
    INFOLOG("parent.value.bstrVal: %ls", parent.value.bstrVal);
    INFOLOG("addressOnParent.value.bstrVal: %ls", addressOnParent.value.bstrVal);
}

int32_t WMIClient::PrepareDiskDriver(const HyperVPlugin::VolumeExtendInfo &diskInfo, IWbemClassObject *controller,
                                     const bool diskDrive, IWbemClassObject *pDiskDriver)
{
    std::vector<WmiMethodParam> params;
    VARIANT controller_path;
    if (GetObjectAttributeInBSTR(controller, "__Path", controller_path) != SUCCESS) {
        ERRLOG("PrepareController GetObjectAttributeInBSTR failed");
        return FAILED;
    }
    if (diskDrive) {
        PrepareDriver(params, controller_path, diskInfo);
    } else {
        VARIANT diskDrivePath;
        VariantInit(&diskDrivePath);
        V_VT(&diskDrivePath) = VT_BSTR;
        V_BSTR(&diskDrivePath) = m_DiskDrivePath;
        std::string s1 = _com_util::ConvertBSTRToString(m_DiskDrivePath);
        std::string s2 = BSTRToString(m_DiskDrivePath);
        WmiMethodParam parent(L"Parent", CIM_STRING, diskDrivePath);
        VARIANT hostResourceArray;
        std::vector<std::string> input;
        std::string convertedPath = ConvertSlash(diskInfo.m_path);
        input.push_back(convertedPath);
        if (GetBSTRArray(input, hostResourceArray) != 0) {
            ERRLOG("PrepareController GetBSTRArray failed");
            return FAILED;
        }
        WmiMethodParam hostResource(L"HostResource", CIM_STRING | CIM_FLAG_ARRAY, hostResourceArray);
        VARIANT vResourceType = String2BSTRVariant(std::to_string(static_cast<int>(HyperVResourceType::LOGICAL_DISK)));
        WmiMethodParam resourceType(L"ResourceType", CIM_UINT16, vResourceType);
        VARIANT vResourceSubType = String2BSTRVariant("Microsoft:Hyper-V:Virtual Hard Disk");
        WmiMethodParam resourceSubType(L"ResourceSubType", CIM_STRING, vResourceSubType);
        INFOLOG("parent.value.bstrVal: %ls", parent.value.bstrVal);
        INFOLOG("diskInfo.m_path: %s, convertedPath: %s", diskInfo.m_path.c_str(), convertedPath.c_str());

        params.push_back(parent);
        params.push_back(hostResource);
        params.push_back(resourceType);
        params.push_back(resourceSubType);
    }
    if (AddParamsToParamCollector(params, pDiskDriver) == FAILED) {
        return FAILED;
    }
    return SUCCESS;
}

int32_t WMIClient::PrepareVMSettings(const std::string &vmId, VARIANT &vmSettingPath)
{
    // Query CIM_VirtualSystemSettingData relate to target VM
    std::vector<IWbemClassObject *> vmSettings;
    std::string setSql = "SELECT * FROM Msvm_VirtualSystemSettingData where __PATH like '%" + vmId + "%'";
    if (ExecQuery(vmSettings, setSql, true) != SUCCESS) {
        ERRLOG("PrepareVMSettings ExecQuery failed");
        return FAILED;
    }
    if (vmSettings.empty()) {
        ERRLOG("Query Msvm_VirtualSystemSettingData result is empty.");
        return FAILED;
    }
    IWbemClassObject *vmSetting = vmSettings[0];

    if (GetObjectAttributeInBSTR(vmSetting, "__Path", vmSettingPath) != 0) {
        ERRLOG("GetObjectAttributeInBSTR failed");
        return FAILED;
    }
    return SUCCESS;
}

VARIANT WMIClient::String2BSTRVariant(const std::string& strValue)
{
    BSTR bstrValue = StringToBSTR(strValue.c_str());
    VARIANT v;
    VariantInit(&v);
    V_VT(&v) = VT_BSTR;
    V_BSTR(&v) = bstrValue;
    return v;
}

int32_t WMIClient::CreateVM(const VmModifyParam &modifyParam, std::string &createdVmId, std::string &errorDetails)
{
    if (Init() == FAILED) {
        ERRLOG("Init failed");
        return FAILED;
    }
    CComPtr<IWbemClassObject> pOutParam = ProcessParamsAndExecMethodForCreateVM(modifyParam, errorDetails);
    if (pOutParam == nullptr) {
        ERRLOG("ProcessParamsAndExecMethodForCreateVM failed!");
        return FAILED;
    }
    VARIANT newVmPath;
    VariantInit(&newVmPath);
    if (FAILED(pOutParam->Get(L"ImportedSystem", 0, &newVmPath, NULL, NULL))) {
        ERRLOG("Get ImportedSystem failed!");
        return FAILED;
    }
    if (newVmPath.bstrVal == nullptr) {
        ERRLOG("Import VM failed.");
        return FAILED;
    }
    BSTR newVmPathBSTR = newVmPath.bstrVal;
    std::string s1 = _com_util::ConvertBSTRToString(newVmPathBSTR);
    std::string s2 = BSTRToString(newVmPathBSTR);
    std::string path = _com_util::ConvertBSTRToString(newVmPath.bstrVal);
    std::string token1 = "\",Name=\"";
    size_t pos = path.find(token1);
    pos += token1.length();
    std::string vmId = path.substr(pos);
    path = vmId;
    pos = path.find("\"");
    vmId = path.substr(0, pos);
    if (ModifyVM(modifyParam, vmId, errorDetails) == FAILED) {
        ERRLOG("ModifyVM failed, errorDetails is: %s", errorDetails.c_str());
        return FAILED;
    }
    createdVmId = vmId;
    return RealizeOrValidatePlannedVM(vmId, true, errorDetails);
}

CComPtr<IWbemClassObject> WMIClient::ProcessParamsAndExecMethodForCreateVM(
    const VmModifyParam &modifyParam, std::string &errorDetails)
{
    WmiMethodExecuteParam executeParam("Msvm_VirtualSystemManagementService", "ImportSystemDefinition");
    VARIANT vmPath = String2BSTRVariant(modifyParam.configFileSrcPath);
    INFOLOG("Using config file: %s", modifyParam.configFileSrcPath.c_str());
    WmiMethodParam systemDefinitionFile(L"SystemDefinitionFile", CIM_STRING, vmPath);
    executeParam.inputeParams.push_back(systemDefinitionFile);

    VARIANT spPath = String2BSTRVariant(modifyParam.snapshotDataRoot);
    WmiMethodParam snapshotPath(L"SnapshotFolder", CIM_STRING, spPath);
    executeParam.inputeParams.push_back(snapshotPath);

    VARIANT generateNew = Bool2BoolVariant(true);
    WmiMethodParam generateNewSystemIdentifier(L"GenerateNewSystemIdentifier", CIM_BOOLEAN, generateNew);
    executeParam.inputeParams.push_back(generateNewSystemIdentifier);
    CComPtr<IWbemClassObject> pOutParam = nullptr;
    if (ExecMethod(executeParam, pOutParam) == FAILED) {
        ERRLOG("ExecuteMethod mehod %s failed, error detail: %s", executeParam.methodName.c_str(),
            executeParam.errDetails.c_str());
        errorDetails = executeParam.errDetails;
        return nullptr;
    }
    return pOutParam;
}

int32_t WMIClient::PrepareModifyParam(const VmModifyParam &param, IWbemClassObject *pVirtualSystemSettingDataInstance)
{
    VARIANT newVMName;
    VariantInit(&newVMName);
    V_VT(&newVMName) = VT_BSTR;
    V_BSTR(&newVMName) = StringToBSTR(param.newVMName.c_str());
    if (pVirtualSystemSettingDataInstance->Put(L"ElementName", 0, &newVMName, CIM_STRING) != SUCCESS) {
        ERRLOG("PrepareModifyParam Put ElementName failed.");
        return FAILED;
    }
    VARIANT configurationDataRoot;
    VariantInit(&configurationDataRoot);
    V_VT(&configurationDataRoot) = VT_BSTR;
    V_BSTR(&configurationDataRoot) = StringToBSTR(param.configFileTgtPath.c_str());
    if (pVirtualSystemSettingDataInstance->Put(L"ConfigurationDataRoot", 0, &configurationDataRoot, CIM_STRING) !=
        SUCCESS) {
        ERRLOG("PrepareModifyParam Put ConfigurationDataRoot failed.");
        return FAILED;
    }
    VARIANT snapshotDataRoot;
    VariantInit(&snapshotDataRoot);
    V_VT(&snapshotDataRoot) = VT_BSTR;
    V_BSTR(&snapshotDataRoot) = StringToBSTR(param.snapshotDataRoot.c_str());
    if (pVirtualSystemSettingDataInstance->Put(L"SnapshotDataRoot", 0, &snapshotDataRoot, CIM_STRING) != SUCCESS) {
        ERRLOG("PrepareModifyParam Put SnapshotDataRoot failed.");
        return FAILED;
    }
    VARIANT swapFileDataRoot;
    VariantInit(&swapFileDataRoot);
    V_VT(&swapFileDataRoot) = VT_BSTR;
    V_BSTR(&swapFileDataRoot) = StringToBSTR(param.swapFileDataRoot.c_str());
    if (pVirtualSystemSettingDataInstance->Put(L"SwapFileDataRoot", 0, &swapFileDataRoot, CIM_STRING) != SUCCESS) {
        ERRLOG("PrepareModifyParam Put SwapFileDataRoot failed.");
        return FAILED;
    }
    return SUCCESS;
}

int32_t WMIClient::ModifyVM(const VmModifyParam &param, const std::string &vmId, std::string &errorDetails)
{
    if (Init() != SUCCESS) {
        errorDetails = "Init failed";
        return FAILED;
    }
    std::vector<HyperVPlugin::VolumeExtendInfo> diskInfos;
    if (DetachDiskFromVM(vmId, diskInfos) == FAILED || DeleteSnapshot(vmId) == FAILED ||
        DisableVMNetwork(vmId, param.disableNetwork) == FAILED) {
        errorDetails = "Prepare job failed";
        return FAILED;
    }
    std::vector<IWbemClassObject *> pObjectVirtualMachines;
    std::string sql = "SELECT * FROM Msvm_VirtualSystemSettingData WHERE VirtualSystemIdentifier='" + vmId + "'";
    if (ExecQuery(pObjectVirtualMachines, sql, true) != SUCCESS) {
        errorDetails = "ModifyVM ExecQuery failed.";
        return FAILED;
    }
    IWbemClassObject *pVirtualSystemSettingDataInstance = pObjectVirtualMachines[0];
    if (PrepareModifyParam(param, pVirtualSystemSettingDataInstance) != SUCCESS) {
        errorDetails = "PrepareModifyParam failed";
        return FAILED;
    }
    std::string settingInStr;
    HRESULT hr = GetObjectTextInSpecifiedFormat(1, pVirtualSystemSettingDataInstance, settingInStr);
    if (FAILED(hr)) {
        errorDetails = "GetObjectTextInSpecifiedFormat  VirtualSystemSettingDataInstance failed";
        return FAILED;
    }
    // 开始修改虚拟机
    std::vector<WmiMethodParam> inputeParams;
    VARIANT settingData;
    VariantInit(&settingData);
    V_VT(&settingData) = VT_BSTR;
    V_BSTR(&settingData) = StringToBSTR(settingInStr.c_str());
    WmiMethodParam systemSettings(L"SystemSettings", CIM_STRING, settingData);
    inputeParams.push_back(systemSettings);

    WmiMethodExecuteParam executeParam("Msvm_VirtualSystemManagementService", "ModifySystemSettings");
    executeParam.inputeParams = inputeParams;
    CComPtr<IWbemClassObject> pOutParam = nullptr;
    if (ExecMethod(executeParam, pOutParam) != SUCCESS) {
        errorDetails = executeParam.errDetails;
        return FAILED;
    }
    return SUCCESS;
}

int32_t WMIClient::ChangeVMPowerState(const std::string &vmId, bool powerOff)
{
    INFOLOG("Handling vm: %s, poweroff: %u", vmId.c_str(), powerOff);
    if (Init() == FAILED) {
        ERRLOG("Init failed");
        return FAILED;
    }
    // Query VM object
    std::vector<IWbemClassObject *> pClassInstances;
    std::string sql = "SELECT * FROM Msvm_ComputerSystem where __PATH like '%" + vmId + "%'";
    if (ExecQuery(pClassInstances, sql, true) != SUCCESS) {
        ERRLOG("ExecMethod ExecQuery failed, sql:%s", sql.c_str());
        return FAILED;
    }
    if (pClassInstances.empty()) {
        ERRLOG("Query ClassInstances result is empty.");
        return FAILED;
    }
    IWbemClassObject *pClassInstance = pClassInstances[0];
    VARIANT classInstancePath;
    if (pClassInstance->Get(L"__Path", 0, &classInstancePath, 0, 0) != SUCCESS) {
        ERRLOG("ExecMethod Get classInstancePath failed");
        return FAILED;
    }

    HRESULT hr;
    int32_t rst = FAILED;

    VARIANT vmTargetStatus;
    VariantInit(&vmTargetStatus);
    V_VT(&vmTargetStatus) = VT_BSTR;
    V_BSTR(&vmTargetStatus) = powerOff ? SysAllocString(L"3") : SysAllocString(L"2");

    std::vector<WmiMethodParam> inputeParams;
    WmiMethodParam requestedState(L"RequestedState", CIM_UINT16, vmTargetStatus);
    inputeParams.push_back(requestedState);

    WmiMethodExecuteParam executeParam("Msvm_ComputerSystem", "RequestStateChange");
    executeParam.inputeParams = inputeParams;
    CComPtr<IWbemClassObject> pOutParam = nullptr;

    if (ExecMethodOnSpecifiedObject(executeParam, pOutParam, classInstancePath) == FAILED) {
        ERRLOG("ExecuteMethod mehod %s failed, error detail: %s", executeParam.methodName.c_str(),
            executeParam.errDetails.c_str());
        std::string invalidStateEC = "32775";
        if (executeParam.errDetails.find(invalidStateEC) != std::string::npos) {
            WARNLOG("Target vm state is not expected or already changed");
            return SUCCESS;
        }
        return FAILED;
    }
    return SUCCESS;
}

int32_t WMIClient::QueryVMDiskList(const std::string &vmId, std::vector<HyperVPlugin::VolumeExtendInfo> &diskInfos)
{
    if (Init() == FAILED) {
        ERRLOG("Init failed");
        return FAILED;
    }
    std::vector<IWbemClassObject *> classObjs;
    std::string sql = "SELECT * FROM CIM_StorageAllocationSettingData where ResourceType = " +
        std::to_string(static_cast<int>(HyperVResourceType::LOGICAL_DISK)) + " and __PATH like '%" + vmId + "%'";
    if (ExecQuery(classObjs, sql, false) != SUCCESS) {
        ERRLOG("QueryVMDiskList ExecQuery failed");
        return FAILED;
    }
    for (const auto &classObj : classObjs) {
        HyperVPlugin::VolumeExtendInfo diskInfo;
        if (SpiltVMDisk(classObj, diskInfo) != SUCCESS) {
            ERRLOG("QueryVMDiskList SpiltVMDisk failed");
            return FAILED;
        }
        VARIANT diskPathArray;
        VariantInit(&diskPathArray);
        if (GetObjectAttributeInBSTR(classObj, "HostResource", diskPathArray) != SUCCESS) {
            ERRLOG("QueryVMDiskList GetObjectAttributeInBSTR failed");
            return FAILED;
        }
        long index = 0;
        BSTR *path;
        SafeArrayAccessData(diskPathArray.parray, (void HUGEP **)&path);
        if (SafeArrayGetElement(diskPathArray.parray, &index, path + index) != SUCCESS) {
            ERRLOG("SafeArrayGetElement failed");
            return FAILED;
        }
        diskInfo.m_path = BSTRToString(path[index]);
        std::vector<IWbemClassObject *> controllers;
        std::string sql = "SELECT * FROM Msvm_ResourceAllocationSettingData where ResourceType=" +
            std::to_string(static_cast<int>(HyperVControllerType::IDE)) + " and __PATH like '%" + vmId + "%" +
            diskInfo.m_controllerUUID + "__0%'";
        if (ExecQuery(controllers, sql, true) != SUCCESS) {
            ERRLOG("QueryVMDiskList ExecQuery failed");
            return FAILED;
        }
        // when query controller type by powershell cmd, 1 stands for SCSI, 0 stands for IDE
        if (controllers.empty()) {
            diskInfo.m_controllerType = "1";
        } else {
            diskInfo.m_controllerType = "0";
        }
        diskInfos.push_back(diskInfo);
    }
    return SUCCESS;
}

int32_t WMIClient::PrepareDiskFromVM(const std::string &vmId,
    const std::vector<HyperVPlugin::VolumeExtendInfo> &diskInfos,
    std::vector<IWbemClassObject *> &pLogicDisks, std::vector<IWbemClassObject *> &pDrives)
{
    if (diskInfos.empty()) {
        INFOLOG("will detach all disks");
        std::string diskSql = "SELECT * FROM CIM_StorageAllocationSettingData where ResourceType=31"
            " and __PATH like '%" +
            vmId + "%'";
        if (ExecQuery(pLogicDisks, diskSql, false) != SUCCESS) {
            ERRLOG("PrepareDiskFromVM ExecQuery diskSql failed, sql:%s", diskSql.c_str());
            return FAILED;
        }
        std::string driveSql = "SELECT * FROM Msvm_ResourceAllocationSettingData WHERE ResourceType=17"
            " and __PATH like '%" +
            vmId + "%'";
        if (ExecQuery(pDrives, driveSql, false) != SUCCESS) {
            ERRLOG("PrepareDiskFromVM ExecQuery driveSql failed, sql:%s", driveSql.c_str());
            return FAILED;
        }
        return SUCCESS;
    }
    for (const auto &diskInfo : diskInfos) {
        INFOLOG("will detach disk: %s, on controller:%s_%s, pos:%s", diskInfo.m_diskIdentifier.c_str(),
            diskInfo.m_controllerUUID.c_str(), diskInfo.m_controllerIndex.c_str(), diskInfo.m_controllerPos.c_str());
        std::vector<IWbemClassObject *> tempDisks;
        std::string diskSql = "SELECT * FROM CIM_StorageAllocationSettingData where ResourceType=31 and"
            " __PATH like '%" +
            vmId + "%'" + " and InstanceID like '%" + diskInfo.m_controllerUUID + "\\\\" + diskInfo.m_controllerIndex +
            "\\\\" + diskInfo.m_controllerPos + "%'";
        if (ExecQuery(tempDisks, diskSql, true) != SUCCESS) {
            ERRLOG("PrepareDiskFromVM ExecQuery diskSql failed, sql:%s", diskSql.c_str());
            return FAILED;
        }
        if (tempDisks.empty()) {
            ERRLOG("Query diskInfo result is empty.");
            return FAILED;
        }
        pLogicDisks.push_back(tempDisks[0]);
        std::vector<IWbemClassObject *> tempDrives;
        std::string driveSql =
            "SELECT * FROM Msvm_ResourceAllocationSettingData WHERE ResourceType=17 and __PATH like '%" + vmId +
            "%'" + " and InstanceID like '%" + diskInfo.m_controllerUUID + "\\\\" +
            diskInfo.m_controllerIndex + "\\\\" + diskInfo.m_controllerPos + "%'";
        if (ExecQuery(tempDrives, driveSql, true) != SUCCESS) {
            ERRLOG("PrepareDiskFromVM ExecQuery driveSql failed, sql:%s", driveSql.c_str());
            return FAILED;
        }
        pDrives.push_back(tempDrives[0]);
    }
    return SUCCESS;
}

int32_t WMIClient::DetachDiskFromVM(const std::string &vmId,
    const std::vector<HyperVPlugin::VolumeExtendInfo> &diskInfos)
{
    INFOLOG("Start detach disk from vm: %s", vmId.c_str());
    if (Init() == FAILED) {
        ERRLOG("Init failed");
        return FAILED;
    }
    std::vector<IWbemClassObject *> pLogicDisks;
    std::vector<IWbemClassObject *> pDrives;
    if (PrepareDiskFromVM(vmId, diskInfos, pLogicDisks, pDrives) != SUCCESS) {
        ERRLOG("PrepareDiskFromVM failed");
        return FAILED;
    }
    std::vector<std::string> strVector;
    // 创建安全数组
    for (const auto &logicDisk : pLogicDisks) {
        VARIANT diskPath;
        logicDisk->Get(L"__Path", 0, &diskPath, 0, 0);
        strVector.push_back(_com_util::ConvertBSTRToString(V_BSTR(&diskPath)));
    }
    for (const auto &drive : pDrives) {
        VARIANT drivePath;
        drive->Get(L"__Path", 0, &drivePath, 0, 0);
        strVector.push_back(_com_util::ConvertBSTRToString(V_BSTR(&drivePath)));
    }
    VARIANT varArray;
    if (GetBSTRArray(strVector, varArray) == FAILED) {
        ERRLOG("DetachDiskFromVM GetBSTRArray failed");
        return FAILED;
    }

    if (V_ARRAY(&varArray)->cbElements == 0) {
        INFOLOG("No need to remove disk");
        return SUCCESS;
    }

    std::vector<WmiMethodParam> inputParams;
    WmiMethodParam resourceSettings(L"ResourceSettings", CIM_REFERENCE | CIM_FLAG_ARRAY, varArray);
    inputParams.push_back(resourceSettings);

    WmiMethodExecuteParam executeParam("Msvm_VirtualSystemManagementService", "RemoveResourceSettings");
    executeParam.inputeParams = inputParams;
    CComPtr<IWbemClassObject> pOutParam = nullptr;
    if (ExecMethod(executeParam, pOutParam) == FAILED) {
        ERRLOG("ExecuteMethod method %s failed", executeParam.methodName.c_str());
        return FAILED;
    }
    for (const auto &diskInfo : diskInfos) {
        int rc = remove(diskInfo.m_path.c_str());
        if (rc != SUCCESS) {
            // failed of deleting disk file should not effect the result of DeleteVm method
            WARNLOG("Remove disk file :%s failed, rc: %d", diskInfo.m_path.c_str(), rc);
        }
    }
    return SUCCESS;
}

int32_t WMIClient::SetNetwork(IWbemClassObject *&netSetting, VARIANT &varArray, bool &isDisableNet)
{
    VARIANT enabledState;
    netSetting->Get(L"EnabledState", 0, &enabledState, 0, 0);
    // 判断是否需要设置禁用网络
    if (V_I4(&enabledState) == DISABLE_CONNECT) {
        INFOLOG("No need to set ethernet connection.");
        isDisableNet = false;
        return SUCCESS;
    }
    V_I4(&enabledState) = DISABLE_CONNECT;
    if (netSetting->Put(L"EnabledState", 0, &enabledState, 0) != SUCCESS) {
        ERRLOG("DisableVMNetwork Put EnabledState failed");
        return FAILED;
    }
    std::string netSettingInStr;
    if (GetObjectTextInSpecifiedFormat(1, netSetting, netSettingInStr) != SUCCESS) {
        ERRLOG("DisableVMNetwork GetObjectTextInSpecifiedFormat failed");
        return FAILED;
    }
    std::vector<std::string> strVector;
    strVector.push_back(netSettingInStr);
    if (GetBSTRArray(strVector, varArray) == FAILED) {
        ERRLOG("DisableVMNetwork GetBSTRArray failed");
        return FAILED;
    }
    return SUCCESS;
}

int32_t WMIClient::DisableVMNetwork(const std::string &vmId, const bool disableNetwork)
{
    if (Init() == FAILED) {
        ERRLOG("Init failed");
        return FAILED;
    }
    // 准备网络连接设置参数
    std::vector<IWbemClassObject *> pNetSettings;
    std::string diskSql =
        "SELECT * FROM CIM_ResourceAllocationSettingData where ResourceType=33 and __PATH like '%" + vmId + "%'";
    if (ExecQuery(pNetSettings, diskSql, false) != SUCCESS) {
        ERRLOG("DisableVMNetwork ExecQuery failed");
        return FAILED;
    }
    for (auto &netSetting : pNetSettings) {
        std::string errorDetails;
        // 用户不禁用网络而且目前计划虚拟机验证通过
        if (!disableNetwork && (RealizeOrValidatePlannedVM(vmId, false, errorDetails) == SUCCESS)) {
            INFOLOG("DisableNetwork is false and ValidatePlannedVM success.");
            return SUCCESS;
        }
        VARIANT varArray;
        bool isDisableNet = true;
        if (SetNetwork(netSetting, varArray, isDisableNet) != SUCCESS) {
            ERRLOG("DisableVMNetwork SetNetwork failed");
            return FAILED;
        }
        // 该网络已是未连接状态，继续下一条
        if (!isDisableNet) {
            continue;
        }
        std::vector<WmiMethodParam> inputeParams;
        WmiMethodParam resourceSettings(L"ResourceSettings", CIM_STRING | CIM_FLAG_ARRAY, varArray);
        inputeParams.push_back(resourceSettings);

        WmiMethodExecuteParam executeParam("Msvm_VirtualSystemManagementService", "ModifyResourceSettings");
        executeParam.inputeParams = inputeParams;
        CComPtr<IWbemClassObject> pOutParam = nullptr;
        if (ExecMethod(executeParam, pOutParam) == FAILED) {
            ERRLOG("ExecuteMethod mehod %s failed, error detail: %s", executeParam.methodName.c_str(),
                executeParam.errDetails.c_str());
            return FAILED;
        }
    }
    return SUCCESS;
}

int32_t WMIClient::DeleteVM(const std::string &vmId)
{
    INFOLOG("Start delete vm: %s", vmId.c_str());
    if (Init() == FAILED) {
        ERRLOG("Init failed");
        return FAILED;
    }
    std::vector<IWbemClassObject *> pComputers;
    std::string sql = "SELECT * FROM CIM_ComputerSystem WHERE name='" + vmId + "'";
    if (ExecQuery(pComputers, sql, true) != SUCCESS) {
        ERRLOG("DeleteVM ExecQuery failed");
        return FAILED;
    }
    if (pComputers.empty()) {
        INFOLOG("Target vm is not exist, no need to handle");
        return SUCCESS;
    }
    std::vector<HyperVPlugin::VolumeExtendInfo> diskInfos;
    if (QueryVMDiskList(vmId, diskInfos) == FAILED) {
        ERRLOG("Get disk info failed");
        return FAILED;
    }

    IWbemClassObject *pComputer = pComputers[0];
    VARIANT vmPath;
    pComputer->Get(L"__Path", 0, &vmPath, 0, 0);

    std::vector<WmiMethodParam> inputeParams;
    WmiMethodParam affectedSystem(L"AffectedSystem", CIM_REFERENCE, vmPath);
    inputeParams.push_back(affectedSystem);

    WmiMethodExecuteParam executeParam("Msvm_VirtualSystemManagementService", "DestroySystem");
    executeParam.inputeParams = inputeParams;
    CComPtr<IWbemClassObject> pOutParam = nullptr;
    if (ExecMethod(executeParam, pOutParam) == FAILED) {
        ERRLOG("ExecuteMethod mehod %s failed, error detail: %s", executeParam.methodName.c_str(),
            executeParam.errDetails.c_str());
        return FAILED;
    }
    int rc = 0;
    for (const auto &diskInfo : diskInfos) {
        rc = remove(diskInfo.m_path.c_str());
        if (rc != SUCCESS) {
            // failed of deleting disk file should not effect the result of DeleteVm method
            WARNLOG("Remove disk file :%s failed, rc: %d", diskInfo.m_path.c_str(), rc);
        }
    }
    return SUCCESS;
}

int32_t WMIClient::RealizeOrValidatePlannedVM(const std::string &vmId, bool isRealize, std::string &errorDetails)
{
    if (Init() == FAILED) {
        ERRLOG("Init failed");
        return FAILED;
    }
    std::vector<IWbemClassObject *> pPlannedVms;
    std::string sql = "SELECT * FROM Msvm_PlannedComputerSystem where Name = '" + vmId + "'";
    if (ExecQuery(pPlannedVms, sql, true) != SUCCESS) {
        ERRLOG("RealizeOrValidatePlannedVM ExecQuery failed");
        return FAILED;
    }
    if (pPlannedVms.empty()) {
        ERRLOG("Query Msvm_PlannedComputerSystem result is empty.");
        return FAILED;
    }
    IWbemClassObject *pPlannedVm = pPlannedVms[0];
    VARIANT path;
    pPlannedVm->Get(L"__Path", 0, &path, 0, 0);
    std::vector<WmiMethodParam> inputeParams;
    WmiMethodParam plannedSystem(L"PlannedSystem", CIM_REFERENCE, path);
    inputeParams.push_back(plannedSystem);

    WmiMethodExecuteParam executeParam("Msvm_VirtualSystemManagementService",
        isRealize ? "RealizePlannedSystem" : "ValidatePlannedSystem");
    executeParam.inputeParams = inputeParams;
    CComPtr<IWbemClassObject> pOutParam = nullptr;
    if (ExecMethod(executeParam, pOutParam) != SUCCESS) {
        ERRLOG("ExecuteMethod mehod %s failed, error detail: %s", executeParam.methodName.c_str(),
            executeParam.errDetails.c_str());
        errorDetails = executeParam.errDetails;
        return FAILED;
    }
    return SUCCESS;
}

int32_t WMIClient::DeleteSnapshot(const std::string &vmId)
{
    INFOLOG("Start delete snapshot of vm %s.", vmId.c_str());
    if (Init() == FAILED) {
        ERRLOG("Init failed");
        return FAILED;
    }
    // 获取快照服务类对象
    int32_t rst = FAILED;
    std::vector<IWbemClassObject *> classObjs;
    std::string sql = "SELECT * FROM Msvm_VirtualSystemSettingData where VirtualSystemIdentifier = '" + vmId +
        "' and VirtualSystemType like '%Microsoft:Hyper-V:Snapshot%' and Parent IS NULL";
    if (ExecQuery(classObjs, sql, false) != SUCCESS) {
        ERRLOG("DeleteSnapshot ExecQuery failed");
        return FAILED;
    }
    for (const auto &pSnapShot : classObjs) {
        VARIANT ref;
        pSnapShot->Get(L"__Path", 0, &ref, 0, 0);

        std::vector<WmiMethodParam> inputeParams;
        WmiMethodParam affectedSnapshot(L"SnapshotSettingData", CIM_REFERENCE, ref);
        inputeParams.push_back(affectedSnapshot);

        WmiMethodExecuteParam executeParam("Msvm_VirtualSystemSnapshotService", "DestroySnapshotTree");
        executeParam.inputeParams = inputeParams;
        CComPtr<IWbemClassObject> pOutParam = nullptr;
        if (ExecMethod(executeParam, pOutParam) == FAILED) {
            ERRLOG("ExecuteMethod mehod %s failed", executeParam.methodName.c_str());
            return FAILED;
        }
    }
    return SUCCESS;
}
#endif